<?php

declare(strict_types=1);

namespace core;

/**
 * 封装 HTTP 请求信息
 *
 * 此类在应用程序启动时被实例化一次，
 * 捕获所有的 HTTP 输入数据 (GET, POST, SERVER, FILES, etc.)。
 *
 * 设计原则:
 * - 不修改原始输入数据
 * - 提供类型安全的访问器
 * - 保持不可变性
 * - 遵循 PSR-7 的部分设计理念
 */
final class Request
{
    private array $get;
    private array $post;
    private array $server;
    private array $files;
    private array $cookie;
    private ?string $rawBody = null;
    private string $path;
    private string $method;
    private ?array $parsedJson = null;

    /**
     * 构造函数
     *
     * @param array $get GET 参数数组
     * @param array $post POST 参数数组
     * @param array $server 服务器变量数组
     * @param array $files 上传文件数组
     * @param array $cookie Cookie 数组
     * @param ?string $rawBody 原始请求体（可选，延迟加载）
     */
    public function __construct(
        array $get,
        array $post,
        array $server,
        array $files,
        array $cookie,
        ?string $rawBody = null
    ) {
        $this->get     = $get;
        $this->post    = $post;
        $this->server  = $server;
        $this->files   = $files;
        $this->cookie  = $cookie;
        $this->rawBody = $rawBody;

        $this->method = strtoupper($server['REQUEST_METHOD'] ?? 'GET');
        $this->path   = $this->normalizePath($server['REQUEST_URI'] ?? '/');
    }

    /**
     * 以静态方式创建 Request 对象
     *
     * 注意：此方法用于生产环境入口，测试时应直接使用构造函数
     */
    public static function capture(): self
    {
        // 预读取原始请求体，因为只能读取一次
        $rawBody = file_get_contents('php://input');

        return new self(
            $_GET,
            $_POST,
            $_SERVER,
            $_FILES,
            $_COOKIE,
            $rawBody
        );
    }

    /**
     * 递归移除斜杠
     */
    private function stripslashesDeep($value): mixed
    {
        if (is_array($value)) {
            return array_map([$this, 'stripslashesDeep'], $value);
        } elseif (is_string($value)) {
            return stripslashes($value);
        }
        return $value;
    }

    /**
     * 标准化请求路径
     */
    private function normalizePath(string $requestUri): string
    {
        $path = parse_url($requestUri, PHP_URL_PATH) ?: '/';

        // 解码并规范化路径
        $path = rawurldecode($path);

        // 防止路径遍历
        $path = str_replace(['../', '..\\'], '', $path);

        // 规范化斜杠
        $path = rtrim($path, '/');
        $path = $path === '' ? '/' : $path;

        return $path;
    }

    /**
     * 获取请求方法 (GET, POST, PUT, DELETE 等)
     */
    public function getMethod(): string
    {
        return $this->method;
    }

    /**
     * 获取请求路径
     *
     * @return string 标准化后的路径，例如 "/users/profile"
     */
    public function getPath(): string
    {
        return $this->path;
    }

    /**
     * 检查请求方法
     */
    public function isMethod(string $method): bool
    {
        return $this->getMethod() === strtoupper($method);
    }

    /**
     * 检查请求是否为 POST
     */
    public function isPost(): bool
    {
        return $this->isMethod('POST');
    }

    /**
     * 检查请求是否为 GET
     */
    public function isGet(): bool
    {
        return $this->isMethod('GET');
    }

    /**
     * 检查是否为 AJAX 请求
     */
    public function isAjax(): bool
    {
        return strtolower($this->getServer('HTTP_X_REQUESTED_WITH', '')) === 'xmlhttprequest';
    }

    /**
     * 检查内容类型是否为 JSON
     */
    public function isJson(): bool
    {
        $contentType = $this->getContentType();
        return $contentType !== null && str_contains(strtolower($contentType), 'application/json');
    }

    /**
     * 获取原始的 php://input 内容
     */
    public function getRawBody(): string
    {
        if ($this->rawBody === null) {
            $this->rawBody = file_get_contents('php://input') ?: '';
        }
        return $this->rawBody;
    }

    /**
     * 获取 JSON 解码后的输入
     *
     * @return array|null 解码后的数组，或在无效 JSON 时返回 null
     */
    public function getJsonBody(): ?array
    {
        if (!$this->isJson()) {
            return null;
        }

        if ($this->parsedJson !== null) {
            return $this->parsedJson;
        }

        $body = $this->getRawBody();
        if (empty($body)) {
            return $this->parsedJson = [];
        }

        $data = json_decode($body, true);
        if (json_last_error() !== JSON_ERROR_NONE) {
            return $this->parsedJson = null;
        }

        return $this->parsedJson = $data;
    }

    /**
     * 获取 Content-Type 头
     */
    public function getContentType(): ?string
    {
        return $this->getServer('CONTENT_TYPE');
    }

    /*--------------------------------------------------
     * $_GET (Query String) 助手
     *--------------------------------------------------*/

    /**
     * 获取 GET 参数
     *
     * @template T
     * @param string $key 参数名
     * @param T $default 默认值
     * @return mixed|T 参数值或默认值
     */
    public function get(string $key, mixed $default = null): mixed
    {
        return $this->get[$key] ?? $default;
    }

    /**
     * 获取整型 GET 参数
     */
    public function getInt(string $key, int $default = 0): int
    {
        $value = $this->get($key);
        if ($value === null || $value === '') {
            return $default;
        }
        $validatedInt = filter_var($value, FILTER_VALIDATE_INT);
        return ($validatedInt !== false) ? $validatedInt : $default;
    }

    /**
     * 获取布尔型 GET 参数
     */
    public function getBool(string $key, bool $default = false): bool
    {
        $value = $this->get($key);
        if ($value === null || $value === '') {
            return $default;
        }

        // 处理常见布尔表示
        if (in_array(strtolower((string)$value), ['true', '1', 'on', 'yes'], true)) {
            return true;
        }
        if (in_array(strtolower((string)$value), ['false', '0', 'off', 'no'], true)) {
            return false;
        }

        return $default;
    }

    /**
     * 获取字符串型 GET 参数
     */
    public function getString(string $key, string $default = ''): string
    {
        $value = $this->get($key);
        if (is_scalar($value)) {
            return (string)$value;
        }
        return $default;
    }

    /*--------------------------------------------------
     * $_POST (Form Data) 助手
     *--------------------------------------------------*/

    /**
     * 获取 POST 参数（内部使用）
     */
    protected function post(string $key, mixed $default = null): mixed
    {
        return $this->post[$key] ?? $default;
    }

    /**
     * 获取字符串型 POST 参数
     *
     * 注意：不对内容进行 HTML 转义，输出时应处理
     */
    public function postString(string $key, string $default = ''): string
    {
        $value = $this->post($key);
        if (!is_string($value)) {
            return $default;
        }
        return trim($value);
    }

    /**
     * 获取整型 POST 参数
     */
    public function postInt(string $key, int $default = 0): int
    {
        $value = $this->post($key);
        if ($value === null || $value === '') {
            return $default;
        }
        $validatedInt = filter_var($value, FILTER_VALIDATE_INT);
        return ($validatedInt !== false) ? $validatedInt : $default;
    }

    /**
     * 获取布尔型 POST 参数
     */
    public function postBool(string $key, bool $default = false): bool
    {
        $value = $this->post($key);
        if ($value === null || $value === '') {
            return $default;
        }

        if (is_bool($value)) {
            return $value;
        }

        // 处理表单复选框等
        if (in_array(strtolower((string)$value), ['true', '1', 'on', 'yes'], true)) {
            return true;
        }
        if (in_array(strtolower((string)$value), ['false', '0', 'off', 'no', ''], true)) {
            return false;
        }

        return $default;
    }

    /**
     * 获取字母数字 POST 参数（仅包含 a-z, A-Z, 0-9, _）
     */
    public function postAlnum(string $key, string $default = ''): string
    {
        $value = $this->postString($key);
        if ($value === '') {
            return $default;
        }
        $cleaned = preg_replace('/[^a-zA-Z0-9_]/', '', $value);
        return $cleaned !== '' ? $cleaned : $default;
    }

    /*--------------------------------------------------
     * 文件上传助手
     *--------------------------------------------------*/

    /**
     * 获取上传文件信息
     *
     * @param string $key 文件字段名
     * @return array|null 文件信息数组或 null（文件不存在时）
     */
    public function getFile(string $key): ?array
    {
        return $this->files[$key] ?? null;
    }

    /**
     * 检查指定字段是否有文件上传
     */
    public function hasFile(string $key): bool
    {
        return isset($this->files[$key]) && is_array($this->files[$key]) &&
            ($this->files[$key]['error'] ?? UPLOAD_ERR_NO_FILE) === UPLOAD_ERR_OK;
    }

    /*--------------------------------------------------
     * $_SERVER 和 Headers 助手
     *--------------------------------------------------*/

    /**
     * 获取 SERVER 变量
     */
    public function getServer(string $key, ?string $default = null): ?string
    {
        return $this->server[$key] ?? $default;
    }

    /**
     * 获取 HTTP 头
     *
     * @param string $header 头名称（不带 HTTP_ 前缀）
     * @param ?string $default 默认值
     * @return ?string 头值或默认值
     */
    public function getHeader(string $header, ?string $default = null): ?string
    {
        $header = str_replace('-', '_', strtoupper($header));
        $serverKey = 'HTTP_' . $header;

        if ($header === 'CONTENT_TYPE' || $header === 'CONTENT_LENGTH') {
            $serverKey = $header;
        }

        return $this->server[$serverKey] ?? $default;
    }

    /**
     * 获取客户端真实 IP 地址
     *
     * @return string 客户端 IP 地址
     */
    public function getIpAddress(): string
    {
        $ipHeaders = [
            'HTTP_CLIENT_IP',
            'HTTP_X_FORWARDED_FOR',
            'HTTP_X_FORWARDED',
            'HTTP_X_CLUSTER_CLIENT_IP',
            'HTTP_FORWARDED_FOR',
            'HTTP_FORWARDED',
            'REMOTE_ADDR'
        ];

        foreach ($ipHeaders as $header) {
            if (!empty($this->server[$header])) {
                // 取第一个非私有IP（如果有多个，通常用逗号分隔）
                $ips = explode(',', $this->server[$header]);
                foreach ($ips as $ip) {
                    $ip = trim($ip);
                    // 跳过保留IP
                    if (!filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE)) {
                        continue;
                    }
                    return $ip;
                }
            }
        }

        return $this->server['REMOTE_ADDR'] ?? '0.0.0.0';
    }

    /**
     * 获取完整 URL（不含查询字符串）
     */
    public function getBaseUrl(): string
    {
        $scheme = $this->isSecure() ? 'https' : 'http';
        $host = $this->getServer('HTTP_HOST', 'localhost');
        return "{$scheme}://{$host}";
    }

    /**
     * 检查是否为 HTTPS 请求
     */
    public function isSecure(): bool
    {
        return $this->getServer('HTTPS') === 'on' ||
            $this->getServer('HTTP_X_FORWARDED_PROTO') === 'https';
    }
}
